package com.learn.shop.service.impl;

import com.learn.shop.mapper.SNovelDetailsMapper;
import com.learn.shop.pojo.entriy.NovelChapterMongo;
import com.learn.shop.pojo.entriy.SNovelCrawlingHistory;
import com.learn.shop.pojo.entriy.SNovelDetails;
import com.learn.shop.pojo.vo.CrawlingNovelVo;
import com.learn.shop.service.CrawlingNovelService;
import com.learn.shop.service.SNovelCrawlingHistoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 解析小说实现类
 */
@Service("CrawlingNovelImplV3")
@Slf4j
public class CrawlingNovelImplV3 implements CrawlingNovelService {

    private static final Logger logger = LoggerFactory.getLogger(CrawlingNovelImplV3.class);

    @Autowired
    private MongoTemplate mongoTemplate;

    @SuppressWarnings("all")
    @Autowired
    private SNovelDetailsMapper sNovelDetailsMapper;

    @Autowired
    private SNovelCrawlingHistoryService sNovelCrawlingHistoryService;


    /**
     * 解析HTML
     * <p>
     * url       小说在网站的首页url
     * novelName 小说名
     * urlSplit  需要切割url来拼接得到章节完整url的切割点
     *
     * @return 返回结果
     * @throws IOException 抛出异常
     */
    @Override
    public synchronized String novel(CrawlingNovelVo crawlingNovelVo) throws IOException {

        String url = crawlingNovelVo.getUrl();
        String novelName = crawlingNovelVo.getNovelName();
        String urlSplit = crawlingNovelVo.getUrlSplit();
        String author = crawlingNovelVo.getAuthor();

        if (StringUtils.isBlank(url) && StringUtils.isBlank(novelName) && StringUtils.isBlank(urlSplit)) {
            return "参数不能为空!";
        }

        logger.info("请求参数->" + "小说名: " + novelName + "  小说地址: " + url + " 域名指定切割位置: " + urlSplit);

        Set<String> collectionNames = mongoTemplate.getCollectionNames();
        logger.info("小说集合: " + collectionNames);
        //判断爬取的小说是否已经存在,存在则先删除
        if (collectionNames.contains(novelName)) {
            mongoTemplate.dropCollection(novelName);
        }

        //删除数据库存在的相同小说
        try {
            sNovelDetailsMapper.deleteByNovelName(novelName);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("解析小说:" + novelName + "-->删除数据库小说报错");
        }


        String parentUrl = null;
        if (StringUtils.isNotBlank(urlSplit)) {
            parentUrl = url.split(urlSplit)[0] + urlSplit;
        } else {
            parentUrl = url;
        }

        int nThreads = 10;
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
        Document doc = Jsoup.connect(url).get();

        //解析开始时间
        long start = System.currentTimeMillis();

        Elements list = Objects.requireNonNull(doc.getElementById("list")).select("dd>a");


        //获取小说作者
        Element info = doc.getElementById("info");
        assert info != null;
        Elements p = info.select("p");
        for (Element element : p) {
            String text = element.text();
            if (text.contains("者")) {
                author = text.split("：")[1];
                crawlingNovelVo.setAuthor(author);
                logger.info(novelName + "--->" + "作者: " + author);
                break;
            }
        }

        //skip扔掉集合的前n个元素
//            List<Element> collect = list.stream().skip(9).collect(Collectors.toList());

        //对得到的小说章节HTML集合遍历
        for (int i = 0; i < list.size(); i++) {
            Element element = list.get(i);
            String childUrl = element.attr("href");
            String title = element.text();
            log.info(title);

            String finalParentUrl = parentUrl;
            Runnable task = () -> {
                log.info("使用线程池分批次解析HTML ： " + Thread.currentThread().getName());
                //调用方法解析章节的HTML
                getNovelContent(title, finalParentUrl, childUrl, novelName);
            };
            //提交线程
            executorService.submit(task);
            try {
                //休眠一毫秒
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
        while (true) {
            //如果线程全部执行完毕
            if (executorService.isTerminated()) {

                //解析结束时间
                long end = System.currentTimeMillis();
                long res = end - start;
                //插入解析的小说到1历史小说表中
                insertNovelHistory(crawlingNovelVo, res);

                SNovelDetails sNovelDetails = new SNovelDetails();
                sNovelDetails.setNovelName(novelName);
                sNovelDetails.setAuthor(author);
                sNovelDetails.setType("01");
                sNovelDetails.setIsend("0");
                try {
                    sNovelDetailsMapper.insert(sNovelDetails);
                    logger.info("小说解析--->新增小数成功!");
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.info("mysql->新增小说失败!");
                }

                logger.info("解析小说成功===========================所有线程结束并关闭!");
                //更新推送状态
                return "解析完毕!";
            }
        }

    }

    public void getNovelContent(String title, String parentUrl, String childUrl, String novelName) {

        try {
            Document document = Jsoup.connect(parentUrl + childUrl)
                    .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
                    .header("Accept-Encoding", "gzip, deflate")
                    .header("Accept-Language", "zh-CN,zh;q=0.9")
                    .header("Cache-Control", "no-cache")
                    .header("Connection", "keep-alive")
                    .header("Pragma", "no-cache")
                    .header("Upgrade-Insecure-Requests", "1")
                    .timeout(8000)
                    .userAgent("Mozilla")//模拟浏览器
                    .get();
            String content = Objects.requireNonNull(document.getElementById("content")).html();
            content = content.replace("<br>", "");
            content = content.replace("&nbsp;", "");
            content = content.replace("无广告手机站: m.shengxu5w.com 同步更新《万古神帝》小说", "");
            NovelChapterMongo mongo = new NovelChapterMongo();
            String novelNo = splitStr(childUrl);
            mongo.setNovelNo(Long.parseLong(novelNo));
            mongo.setChapterName(title);
            mongo.setText(content);
            mongoTemplate.save(mongo, novelName);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("解析小说失败!");
        }
    }


    //切割字符串
    public String splitStr(String str) {
        String resStr = "";
        if (StringUtils.isNotBlank(str)) {
            if (str.contains("/")) {
                String[] split = str.split("/");
                resStr = split[split.length - 1].split("\\.")[0];
            } else {
                resStr = str.split("\\.")[0];
            }
        }
        return resStr;
    }

    public void insertNovelHistory(CrawlingNovelVo crawlingNovelVo, long time) {

        SNovelCrawlingHistory history = new SNovelCrawlingHistory();
        history.setNovelName(crawlingNovelVo.getNovelName());
        history.setAuthor(crawlingNovelVo.getAuthor());
        history.setUrl(crawlingNovelVo.getUrl());
        history.setUrlSplit(crawlingNovelVo.getUrlSplit());
        history.setCrawlingTime(new Date());
        history.setConsumeTime(getConsumeTime(time));
        try {
            sNovelCrawlingHistoryService.insert(history);
            logger.info("小说解析--->新增小说到历史表成功!");
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("插入小说到历史表失败!");
        }
    }

    public String getConsumeTime(long time) {
        int result = (int) (time / (1000));
        int m = result / 60;
        int s = result % 60;
        return m + "分 " + s + "秒";
    }

}
